/**********************************************************************************
// Asteroids
//
// Creation:	14 Set 2008
// Updated:		23 Abr 2011
// Compiler:	Visual Studio 2010
//
// Notes:		Test the Anabele engine
//				Game inspired on classical asteroids game
//
**********************************************************************************/

#include "Asteroids.h"

/**********************************************************************************/

void Asteroids::GameInit()
{
	// -----------------------------
	// read game records from file
	// -----------------------------

	// open file
	std::ifstream fin;
	fin.open("records.txt");

	// if the openning fails
	if (!fin.is_open())
	{
		// file records.txt does not exist
		// set default records
		records[0] = 6.0f;
		records[1] = 12.0f;
		records[2] = 16.0f;
		records[3] = 22.0f;
		overallRecord = 58.0f;
	}
	else
	{
		// opening succeeded
		// read records from file
		for (int i=0; i <= NUMLEVELS; ++i)
			fin >> records[i];

		fin >> overallRecord;
	}
	fin.close();

	// ---------------------------

	// initialize player time
	playerTime = 10.0f;

	// get coordinates for the center of the screen
	midX = GetWindowWidth()/2 - 1;
	midY = GetWindowHeight()/2 - 1;

	// position of level result box
	finPosX = 0;
	finPosY = 0;

	// get frequency of high performance counter
	QueryPerformanceFrequency(&freq);

	// the borders are positioned around the screen
	// they ensure every missile will collide with something
	// and than no need to test "out of screen" for missiles
	border[0] = Rect(0, 0, GetWindowWidth()-1.0f, 50);
	border[0].MoveTo(0, -50);
	border[0].customType = objBorder;
	border[0].color = Color(128,128,128);
	scene.RegisterStatic(border[0]);

	border[1] = Rect(0, 0, GetWindowWidth()-1.0f, 50);
	border[1].MoveTo(0.0f, GetWindowHeight()-1.0f);
	border[1].customType = objBorder;
	border[1].color = Color(128,128,128);
	scene.RegisterStatic(border[1]);

	border[2] = Rect(0, 0, 50, GetWindowHeight()-1.0f);
	border[2].MoveTo(-50, 0);
	border[2].customType = objBorder;
	border[2].color = Color(128,128,128);
	scene.RegisterStatic(border[2]);

	border[3] = Rect(0, 0, 50, GetWindowHeight()-1.0f);
	border[3].MoveTo(GetWindowWidth()-1.0f, 0.0f);
	border[3].customType = objBorder;
	border[3].color = Color(128,128,128);
	scene.RegisterStatic(border[3]);

	// initialize and load sound effects into memory
	snd.Initialize();	

	snd.Load("Resources\\JetWash.wav");
	snd.Frequency(UP,44100);
	
	snd.Load("Resources\\JetWash.wav");
	snd.Frequency(DOWN,22050);
	snd.Volume(DOWN, 9500);

	snd.Load("Resources\\Pulse.wav");
	snd.Frequency(PULSE, 11025);
	snd.Volume(PULSE, 9000);

	snd.Load("Resources\\Explosion.wav");
	snd.Frequency(EXPLOSION,11025);
	snd.Volume(EXPLOSION, 9000);

	snd.Load("Resources\\Explosion.wav");
	snd.Frequency(MISSILEHIT,11025);
	snd.Volume(MISSILEHIT, 8800);

	snd.Load("Resources\\Blow.wav");
	snd.Frequency(BLOW,22050);
	snd.Volume(BLOW, 8000);

	snd.Load("Resources\\Beep.wav");
	snd.Frequency(BEEP,11025);
	snd.Volume(BEEP, 15000);

	snd.Load("Resources\\Intro.wav");
	snd.Frequency(INTRO, 11025);
	snd.Volume(INTRO, 10000);

	snd.Load("Resources\\Transition.wav");
	snd.Frequency(TRANSITION,11025);
	snd.Volume(TRANSITION, 9200);

	snd.Load("Resources\\LevelUp.wav");
	snd.Frequency(LEVELUP, 11025);
	snd.Volume(LEVELUP, 9000);

	input.Initialize();
	input.InitializeKeyboard();

	// load device dependent resources
	LoadResources();

	// initialize star field here
	eSpace = new StarField(GetWindowWidth(), GetWindowHeight(), NUM_STARS);

	// play intro music
	snd.Play(INTRO, 1);

	// initial game state
	prevState = HOME;
	gameState = HOME;
}

/**********************************************************************************/
// load resources when the device is operational

void Asteroids::LoadResources() 
{
	// load bitmap for the game title
	logo = new Sprite("Resources\\SpaceCleaner.tga", 0, 0, 1, Color(16,16,16));
	logo->MoveTo(GetWindowWidth()/2.0f, GetWindowHeight()/2.0f);
	
	// take lost time out of start time for proper timming
	long long lostTime = GetLostDevTime().QuadPart - start.QuadPart; 
	QueryPerformanceCounter(&start);
	start.QuadPart = start.QuadPart - lostTime;
}	

/**********************************************************************************/
// unload resources when device is lost

void Asteroids::UnloadResources()
{
	delete logo;
}


/**********************************************************************************/

void Asteroids::GameShutdown()
{
	if (eSpace)
		delete eSpace;
}

/**********************************************************************************/

void Asteroids::GameLogic()
{
	// Update keyboard state
	input.GetKeyboardState();

	switch (gameState)
	{
	case HOME:
		// Quits by pressing ESC
		EndsOnEscape();

		GameTitleLogic();
		// initialize Level 1
		if (gameState == PLEVEL1)
			Level1Init();
		break;

	case PLEVEL1:
	case LEVEL1: 	
		// Quits by pressing ESC
		EndsOnEscape();

		Level1Logic();

		// cleanup before changing state
		if ((gameState != LEVEL1) && (gameState != PLEVEL1))
		{
			prevState = LEVEL1;
			// stop selected sounds
			// if state change happens while a sound is playing
			// we need to stop the sound before going to the new state
			snd.Stop(UP);
			snd.Stop(DOWN);
			snd.Stop(PULSE);
		}   
		break;

	case PLEVEL2:
	case LEVEL2: 	
		// Quits by pressing ESC
		EndsOnEscape();

		Level2Logic();

		// cleanup before changing state
		if ((gameState != LEVEL2) && (gameState != PLEVEL2))
		{
			prevState = LEVEL2;
			// stop selected sounds
			// if state change happens while a sound is playing
			// we need to stop the sound before going to the new state
			snd.Stop(UP);
			snd.Stop(DOWN);
			snd.Stop(PULSE);
		}   
		break;

	case PLEVEL3:
	case LEVEL3: 	
		// Quits by pressing ESC
		EndsOnEscape();

		Level3Logic();

		// cleanup before changing state
		if ((gameState != LEVEL3) && (gameState != PLEVEL3))
		{
			prevState = LEVEL3;
			// stop selected sounds
			// if state change happens while a sound is playing
			// we need to stop the sound before going to the new state
			snd.Stop(UP);
			snd.Stop(DOWN);
			snd.Stop(PULSE);
		}   
		break;

	case PLEVEL4:
	case LEVEL4: 
		// Quits by pressing ESC
		EndsOnEscape();

		Level4Logic();
		
		// cleanup before changing state
		if ((gameState != LEVEL4) && (gameState != PLEVEL4))
		{
			prevState = LEVEL4;

			// stop selected sounds
			// if state change happens while a sound is playing
			// we need to stop the sound before going to the new state
			snd.Stop(UP);
			snd.Stop(DOWN);
			snd.Stop(PULSE);
		}

		break;
	
	case GAMECOMPLETE:
		GameCompleteLogic();

		// if player quit game complete screen
		if (gameState != GAMECOMPLETE)
		{
			if (playerOverall < overallRecord)
			{
				// set new overall record 
				overallRecord = playerOverall;

				// open record file
				std::ofstream fout;
				fout.open("records.txt");
				if (!fout.is_open())
					exit(EXIT_FAILURE);
				
				// write records to file
				for (int i=0; i < NUMLEVELS; i++)
					fout << records[i] << std::endl;
				fout << overallRecord;
				
				// close file
				fout.close();
			}

			// clean up
			switch (prevState)
			{
			case LEVEL1: Level1CleanUp(); break;
			case LEVEL2: Level2CleanUp(); break;
			case LEVEL3: Level3CleanUp(); break;
			case LEVEL4: Level4CleanUp(); break;
			} 
		}
		break;

	case LEVELCOMPLETE:
		LevelCompleteLogic();

		// if player quit level complete screen
		if (gameState != LEVELCOMPLETE)
		{
			// level numbers are even numbers starting at two
			// transform level numbers in vector index
			int indLevel = prevState/2-1;
			// register player history across levels
			playerHistory[indLevel] = playerTime;

			// calculate the overall player time 
			playerOverall = 0.0f;
			for (int i=0; i <= indLevel; ++i)
				playerOverall += playerHistory[i];

			// if player set a new record, write it to file
			if (playerTime < records[indLevel])
			{
				// update records
				records[indLevel] = playerTime;

				// open records file
				std::ofstream fout;
				fout.open("records.txt");
				if (!fout.is_open())
					exit(EXIT_FAILURE);
				
				// write records to file
				for (int i=0; i < NUMLEVELS; i++)
					fout << records[i] << std::endl;
				
				// write overall record
				fout << overallRecord;
				
				// close file
				fout.close();
			}

			// calculate the theoretical best overall time
			theoreticalBest = 0.0f;
			for (int i=0; i <= indLevel; ++i)
				theoreticalBest += records[i];

			// clean up
			switch (prevState)
			{
			case LEVEL1: Level1CleanUp(); break;
			case LEVEL2: Level2CleanUp(); break;
			case LEVEL3: Level3CleanUp(); break;
			case LEVEL4: Level4CleanUp(); break;
			}

			// initialize next level
			switch (gameState)
			{
			case PLEVEL1: 
				Level1Init(); 
				break;
			case PLEVEL2:
				Level2Init(); 
				break;
			case PLEVEL3:
				Level3Init(); 
				break;
			case PLEVEL4:
				Level4Init(); 
				break;
			case GAMECOMPLETE:
				// reset finish marker position 
				// to be used on GAMECOMPLETE screen
				finish.MoveTo(midX, midY-55);
				break;
			}
		}
		break;

	case GAMEOVER:
		// Quits by pressing ESC
		EndsOnEscape();

		GameOverLogic();

		if (gameState != GAMEOVER)
		{
			switch(prevState)
			{
			case LEVEL1: Level1CleanUp(); break;
			case LEVEL2: Level2CleanUp(); break;
			case LEVEL3: Level3CleanUp(); break;
			case LEVEL4: Level4CleanUp(); break;
			}

			prevState = GAMEOVER;

			// initialize level 
			// in case of level restarted
			switch (gameState)
			{
			case LEVEL1: 
				gameState = PLEVEL1; 
				Level1Init();
				break;
			case LEVEL2: 
				gameState = PLEVEL2; 
				Level2Init();
				break;
			case LEVEL3: 
				gameState = PLEVEL3; 
				Level3Init();
				break;
			case LEVEL4: 
				gameState = PLEVEL4; 
				Level4Init();
				break;
			}
		}
		break;
	}
} 

/**********************************************************************************/

void Asteroids::GameDraw()
{
	// Lock screen for pixel plotting functions
	LockScreen();

	switch (gameState)
	{
	case HOME:
		GameTitleDraw();
		// draw game logo
		logo->Draw();
		break;

	case PLEVEL1:
	case LEVEL1: 
		Level1Draw(); 
		break;

	case PLEVEL2:
	case LEVEL2: 
		Level2Draw(); 
		break;

	case PLEVEL3:
	case LEVEL3: 
		Level3Draw(); 
		break;

	case PLEVEL4:
	case LEVEL4: 
		Level4Draw(); 
		break;
		
	case GAMECOMPLETE:
		GameCompleteDraw();
		break;

	case LEVELCOMPLETE:
		switch(prevState)
		{
		case LEVEL1: Level1Draw(); break;
		case LEVEL2: Level2Draw(); break;
		case LEVEL3: Level3Draw(); break;
		case LEVEL4: Level4Draw(); break;
  		}
		LevelCompleteDraw();
		break;

	case GAMEOVER:
		switch(prevState)
		{
		case LEVEL1: Level1Draw(); break;
		case LEVEL2: Level2Draw(); break;
		case LEVEL3: Level3Draw(); break;
		case LEVEL4: Level4Draw(); break;
		}
		GameOverDraw();
		break;
	}

	// UnLock screen when pixel plotting is done
	UnLockScreen();
} 


/**********************************************************************************/

/**********************************************************************************/
/*                                  WinMain                                       */
/**********************************************************************************/

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	Asteroids astGame;

	//astGame.SetWindowMode(WINDOWED);
	//astGame.SetWindowWidth(1024);
	//astGame.SetWindowHeight(768);
	astGame.SetWindowBgColor(16,16,16);
	astGame.SetWindowTitle(string("Space Race"));
	astGame.SetWindowIcon(LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON)));
	astGame.ShowWindowCursor(false);
	astGame.Initialize(hInstance);
	astGame.Start();
	
	return 0;

}

/**********************************************************************************/